package cn.ibizlab.core.authentication.service.impl;

import cn.ibizlab.core.ad.domain.SysPerson;
import cn.ibizlab.core.authentication.constants.AuthenticationConstant;
import cn.ibizlab.core.authentication.domain.AuthProvider;
import cn.ibizlab.core.authentication.domain.AuthUserLogin;
import cn.ibizlab.core.authentication.provider.LdapAuthenticationServiceFactory;
import cn.ibizlab.core.open.domain.OpenAccessAuth;
import cn.ibizlab.util.enums.Entities;
import cn.ibizlab.util.errors.BadRequestAlertException;
import cn.ibizlab.util.security.JwkStore;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.security.PrivateKey;
import java.util.Date;

@Service
public class AuthenticationService {

    @Autowired
    @Lazy
    private JwkStore jwkStore;
    @Autowired
    AuthProviderServiceImpl authProviderService;
    @Autowired
    @Lazy
    private LdapAuthenticationServiceFactory ldapAuthenticationServiceFactory;

    public String generateTokenByRSA(Long expiration) {
        try {
            PrivateKey privateJWK = jwkStore.getPrivateKey();
            Date now = new Date();
            JWSSigner signer = new RSASSASigner(privateJWK);
            JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256)
                    .contentType("JWS")
                    .build();
            JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                    .issueTime(now)
                    .expirationTime(new Date(now.getTime()+expiration))
                    .build();
            SignedJWT signedJWT = new SignedJWT(header,claimsSet);
            signedJWT.sign(signer);
            return signedJWT.serialize();
        } catch (Exception e) {
            throw new BadRequestAlertException("生成jwt失败:"+e.getMessage(),Entities.AUTH_USER.toString(),"generateToken");
        }
    }

    public Boolean validateTokenByRSA(String token){
        try {
            Date now = new Date();
            JWSVerifier verifier = jwkStore.getJWSVerifier();
            SignedJWT signedJWT = SignedJWT.parse(token);
            JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();

            if(!signedJWT.verify(verifier))
                return false;

            if(now.after(claimsSet.getExpirationTime()))
                return false;

        } catch (Exception e) {
            throw new BadRequestAlertException("解析jwt失败，请确认数据格式是否正确:"+e.getMessage(),Entities.AUTH_USER.toString(),"validateToken");
        }
        return true;
    }

    /**
     * 同步变更密码
     * @param oldPassword
     * @param newPassword
     * @return
     */
    public Boolean syncChangePwd(SysPerson person, String oldPassword, String newPassword) {
        if (ObjectUtils.isEmpty(newPassword))
            return false;

        AuthProvider provider = authProviderService.first(AuthenticationConstant.PROVIDER_LDAP);
        if(provider == null)
            return false;

        LdapAuthenticationService ldapAuthenticationService = (LdapAuthenticationService) ldapAuthenticationServiceFactory.getAuthenticationService(provider);
        SysPerson ldapUser = ldapAuthenticationService.lookup(person.getUid());
        if(ldapUser != null){
            ldapUser.setUserPassword(newPassword);
            ldapAuthenticationService.update(ldapUser,"userPassword");

            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(person.getUid(),newPassword,null);
            Authentication result = ldapAuthenticationService.authenticate(authentication);
            if(result == null)
                throw new BadRequestAlertException("ldap 变更密码失败", Entities.AUTH_LOGIN.toString(), "changePwd");
        }
        return true;

    }

    @Cacheable(value = "ibzuaa_users", key = "'imageCaptcha:'+#p0")
    public String getImageCaptcha(String imageCaptchaId){
        return null;
    }

    @CachePut(value = "ibzuaa_users", key = "'imageCaptcha:'+#p0")
    public String putImageCaptcha(String imageCaptchaId, String imageCaptchaText){
        return imageCaptchaText;
    }

    @CacheEvict(value = "ibzuaa_users", key = "'imageCaptcha:'+#p0")
    public void resetImageCaptcha(String imageCaptchaId){}

    @Cacheable(value = "ibzuaa_users", key = "'authUserLogin:'+#p0")
    public AuthUserLogin getAuthUserLogin(String loginName){
        return null;
    }

    @CachePut(value = "ibzuaa_users", key = "'authUserLogin:'+#p0")
    public AuthUserLogin putAuthUserLogin(String loginName, AuthUserLogin authUserLogin){
        return authUserLogin;
    }

    @CacheEvict(value = "ibzuaa_users", key = "'authUserLogin:'+#p0")
    public void resetAuthUserLogin(String loginName){}


    @Cacheable(value = "ibzuaa_users", key = "'smsCode:'+#p0")
    public String getSMSCode(String phone){
        return null;
    }

    @CachePut(value = "ibzuaa_users", key = "'smsCode:'+#p0")
    public String putSMSCode(String phone, String smsCode){
        return smsCode;
    }

    @CacheEvict(value = "ibzuaa_users", key = "'smsCode:'+#p0")
    public void resetSMSCode(String phone){}

    @Cacheable(value = "ibzuaa_users", key = "'socialUser:'+#p0")
    public OpenAccessAuth getSocialUser(String socialUserId){
        return null;
    }

    @CachePut(value = "ibzuaa_users", key = "'socialUser:'+#p0")
    public OpenAccessAuth putSocialUser(String socialUserId, OpenAccessAuth socialUser){
        return socialUser;
    }

    @CacheEvict(value = "ibzuaa_users", key = "'socialUser:'+#p0")
    public void resetSocialUser(String socialUserId){}
}
